﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;

using Suplex.General;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
using System.Windows.Forms;
using System.IO;

namespace sprocket
{
	public class Settings
	{
		private string _tableListCommandOverride = string.Empty;

		public Settings()
		{
			this.RecentFiles = new List<string>();
			this.RecentConnections = new List<ConnectionData>();
		}

		public List<string> RecentFiles { get; set; }
		public List<ConnectionData> RecentConnections { get; set; }
		public System.Drawing.Point FileToolbar { get; set; }
		public System.Drawing.Point ConnectionToolbar { get; set; }
		public System.Drawing.Point ExecuteToolbar { get; set; }
		public bool IsAdvancedMode { get; set; }

		[XmlIgnore()]
		public string TableListCommandOverride
		{
			get { return _tableListCommandOverride; }
			set { _tableListCommandOverride = value; }
		}

		[XmlAnyElement( "TableListCommandOverride" )]
		public XmlElement TableListCommandOverrideData
		{
			get
			{
				XmlDocument d = new XmlDocument();
				XmlElement e = d.CreateElement( "TableListCommandOverride" );
				e.AppendChild( d.CreateCDataSection( _tableListCommandOverride ) );
				return e;
			}
			set
			{
				_tableListCommandOverride = value.InnerText.Replace( "\n", "\r\n" );
			}
		}


		[XmlIgnore()]
		private static string FileName { get { return string.Format( @"{0}\sqlsprocket_settings.txt", Application.StartupPath ); } }

		public void Serialize()
		{
			XmlUtils.Serialize<Settings>( this, FileName );
		}

		public static Settings Deserialize()
		{
			if( File.Exists( FileName ) )
			{
				return XmlUtils.Deserialize<Settings>( FileName );
			}
			else
			{
				return new Settings();
			}
		}
	}

	public class ConnectionData
	{
		public string Server { get; set; }
		public string Database { get; set; }
		public string UserName { get; set; }

		public override string ToString()
		{
			string username = string.Empty;
			if( !string.IsNullOrEmpty( this.UserName ) )
			{
				username = string.Format( ".{0}", this.UserName );
			}
			return string.Format( "{0}.{1}{2}", this.Server, this.Database, username );
		}
	}

	public enum TemplateType
	{
		GlobalData,
		TableAwareData,
		PostProcessData,
		CSharpCode,
		TSqlCode,
		Output
	}

	public class Template : INotifyPropertyChanged
	{
		private string _name = string.Empty;
		private TemplateType _templateType = TemplateType.Output;
		private string _scriptTemplate = string.Empty;

		public Template()
		{
			this.Replacements = new FindReplaceCollection();
		}

		public string Name
		{
			get { return _name; }
			set
			{
				if( _name != value )
				{
					_name = value;
					this.OnPropertyChanged( "Name" );
				}
			}
		}

		public TemplateType TemplateType
		{
			get { return _templateType; }
			set
			{
				if( _templateType != value )
				{
					_templateType = value;
					this.OnPropertyChanged( "TemplateType" );
				}
			}
		}

		[XmlIgnore()]
		public string ScriptTemplate
		{
			get { return _scriptTemplate; }
			set
			{
				if( _scriptTemplate != value )
				{
					_scriptTemplate = value;
					this.OnPropertyChanged( "ScriptTemplate" );
				}
			}
		}

		[XmlAnyElement( "ScriptTemplate" )]
		public XmlElement ScriptTemplateData
		{
			get
			{
				XmlDocument d = new XmlDocument();
				XmlElement e = d.CreateElement( "ScriptTemplate" );
				e.AppendChild( d.CreateCDataSection( _scriptTemplate ) );
				return e;
			}
			set
			{
				_scriptTemplate = value.InnerText.Replace( "\n", "\r\n" );
			}
		}

		[XmlIgnore()]
		public bool IsChecked { get; set; }

		public FindReplaceCollection Replacements { get; set; }

		public override string ToString()
		{
			return this.Name;
		}


		#region INotifyPropertyChanged, INotifyPropertyChanging Members
		public event PropertyChangedEventHandler PropertyChanged;
		public event PropertyChangingEventHandler PropertyChanging;

		protected void OnPropertyChanged(string propertyName)
		{
			if( PropertyChanged != null )
			{
				PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
			}
		}

		protected void OnPropertyChanging(string propertyName)
		{
			if( PropertyChanging != null )
			{
				PropertyChanging( this, new PropertyChangingEventArgs( propertyName ) );
			}
		}
		#endregion
	}

	public class FindReplace
	{
		public string Find { get; set; }
		public string Replace { get; set; }
	}

	public class FindReplaceCollection : List<FindReplace> { }

	public class TemplateList : ObservableCollection<Template>
	{
		private TemplateCollection _parentList = null;

		public TemplateList(TemplateCollection parentList)
		{
			_parentList = parentList;
		}

		//protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
		//{
		//    switch( e.Action )
		//    {
		//        case NotifyCollectionChangedAction.Add:
		//        {
		//            this.AddTemplates( e.NewItems );
		//            break;
		//        }
		//        case NotifyCollectionChangedAction.Replace:
		//        case NotifyCollectionChangedAction.Reset:
		//        {
		//            this.AddTemplates( e.NewItems );
		//            this.RemoveTemplates( e.OldItems );
		//            break;
		//        }
		//        case NotifyCollectionChangedAction.Remove:
		//        {
		//            this.RemoveTemplates( e.OldItems );
		//            break;
		//        }
		//    }

		//    base.OnCollectionChanged( e );
		//}

		//private void AddTemplates(IList templates)
		//{
		//    _parentList.SuppressChangedEvent = true;
		//    foreach( Template t in templates )
		//    {
		//        _parentList.Add( t );
		//    }
		//    _parentList.SuppressChangedEvent = false;
		//}

		//private void RemoveTemplates(IList templates)
		//{
		//    _parentList.SuppressChangedEvent = true;
		//    foreach( Template t in templates )
		//    {
		//        _parentList.Remove( t );
		//    }
		//    _parentList.SuppressChangedEvent = false;
		//}
	}

	public class TemplateCollection : ObservableCollection<Template>
	{
		private TemplateListFile _parentList = null;

		public TemplateCollection(TemplateListFile parentList)
		{
			_parentList = parentList;
		}

		internal bool SuppressChangedEvent { get; set; }

		protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
		{
			if( !this.SuppressChangedEvent )
			{
				switch( e.Action )
				{
					case NotifyCollectionChangedAction.Add:
					{
						this.AddTemplates( e.NewItems );
						break;
					}
					case NotifyCollectionChangedAction.Replace:
					case NotifyCollectionChangedAction.Reset:
					{
						this.AddTemplates( e.NewItems );
						this.RemoveTemplates( e.OldItems );
						break;
					}
					case NotifyCollectionChangedAction.Remove:
					{
						this.RemoveTemplates( e.OldItems );
						break;
					}
				}
			}

			base.OnCollectionChanged( e );
		}

		private void AddTemplates(IList templates)
		{
			foreach( Template t in templates )
			{
				switch( t.TemplateType )
				{
					case TemplateType.GlobalData:
					{
						_parentList.GlobalDataList.Add( t );
						_parentList.DataList.Add( t );
						break;
					}
					case TemplateType.TableAwareData:
					{
						_parentList.TableAwareDataList.Add( t );
						_parentList.DataList.Add( t );
						break;
					}
					case TemplateType.PostProcessData:
					{
						_parentList.PostProcessDataList.Add( t );
						_parentList.DataList.Add( t );
						break;
					}
					case TemplateType.CSharpCode:
					case TemplateType.TSqlCode:
					{
						_parentList.CodeList.Add( t );
						break;
					}
					case TemplateType.Output:
					{
						_parentList.OutputList.Add( t );
						break;
					}
				}
			}
		}

		private void RemoveTemplates(IList templates)
		{
			foreach( Template t in templates )
			{
				switch( t.TemplateType )
				{
					case TemplateType.GlobalData:
					{
						_parentList.GlobalDataList.Remove( t );
						_parentList.DataList.Remove( t );
						break;
					}
					case TemplateType.TableAwareData:
					{
						_parentList.TableAwareDataList.Remove( t );
						_parentList.DataList.Remove( t );
						break;
					}
					case TemplateType.PostProcessData:
					{
						_parentList.PostProcessDataList.Remove( t );
						_parentList.DataList.Remove( t );
						break;
					}
					case TemplateType.CSharpCode:
					case TemplateType.TSqlCode:
					{
						_parentList.CodeList.Remove( t );
						break;
					}
					case TemplateType.Output:
					{
						_parentList.OutputList.Remove( t );
						break;
					}
				}
			}
		}
	}

	public class TemplateListFile
	{
		private bool _isDirty = false;

		public TemplateListFile()
		{
			this.Templates = new TemplateCollection( this );
			this.GlobalDataList = new TemplateList( this.Templates );
			this.TableAwareDataList = new TemplateList( this.Templates );
			this.PostProcessDataList = new TemplateList( this.Templates );
			this.DataList = new TemplateList( this.Templates );
			this.CodeList = new TemplateList( this.Templates );
			this.OutputList = new TemplateList( this.Templates );

			//this.Templates.CollectionChanged += new NotifyCollectionChangedEventHandler( Templates_CollectionChanged );
			this.DataList.CollectionChanged += new NotifyCollectionChangedEventHandler( Templates_CollectionChanged );
			this.CodeList.CollectionChanged += new NotifyCollectionChangedEventHandler( Templates_CollectionChanged );
			this.OutputList.CollectionChanged += new NotifyCollectionChangedEventHandler( Templates_CollectionChanged );
		}

		void Templates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			this.IsDirty = true;

			switch( e.Action )
			{
				case NotifyCollectionChangedAction.Add:
				{
					foreach( Template t in e.NewItems ) { t.PropertyChanged += new PropertyChangedEventHandler( Template_PropertyChanged ); }
					break;
				}
				case NotifyCollectionChangedAction.Replace:
				case NotifyCollectionChangedAction.Reset:
				{
					foreach( Template t in e.NewItems ) { t.PropertyChanged += new PropertyChangedEventHandler( Template_PropertyChanged ); }
					foreach( Template t in e.OldItems ) { t.PropertyChanged -= Template_PropertyChanged; }
					break;
				}
				case NotifyCollectionChangedAction.Remove:
				{
					foreach( Template t in e.OldItems ) { t.PropertyChanged -= Template_PropertyChanged; }
					break;
				}
			}
		}

		void Template_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			this.IsDirty = true;
		}

		public TemplateCollection Templates { get; set; }

		[XmlIgnore()]
		public TemplateList GlobalDataList { get; internal set; }
		[XmlIgnore()]
		public TemplateList TableAwareDataList { get; internal set; }
		[XmlIgnore()]
		public TemplateList PostProcessDataList { get; internal set; }
		[XmlIgnore()]
		public TemplateList DataList { get; internal set; }
		[XmlIgnore()]
		public TemplateList CodeList { get; internal set; }
		[XmlIgnore()]
		public TemplateList OutputList { get; internal set; }

		[XmlIgnore()]
		public string FileName { get; set; }
		[XmlIgnore()]
		public bool IsDirty
		{
			get { return _isDirty; }
			set
			{
				if( _isDirty != value )
				{
					_isDirty = value;
					this.OnIsDirtyChanged();
				}
			}
		}

		public void Serialize()
		{
			this.Templates.SuppressChangedEvent = true;
			this.Templates.Clear();
			foreach( Template t in this.DataList ) { this.Templates.Add( t ); }
			foreach( Template t in this.CodeList ) { this.Templates.Add( t ); }
			foreach( Template t in this.OutputList ) { this.Templates.Add( t ); }
			this.Templates.SuppressChangedEvent = false;
			XmlUtils.Serialize<TemplateListFile>( this, this.FileName );
		}

		public static TemplateListFile Deserialize(string filePath)
		{
			TemplateListFile templateListFile = XmlUtils.Deserialize<TemplateListFile>( filePath );
			templateListFile.FileName = filePath;
			templateListFile.IsDirty = false;
			return templateListFile;
		}

		#region IsDirtyChanged
		public event EventHandler IsDirtyChanged;

		protected void OnIsDirtyChanged()
		{
			if( IsDirtyChanged != null )
			{
				IsDirtyChanged( this, EventArgs.Empty );
			}
		}
		#endregion
	}
}